package cm.tl.pms.util;

import org.apache.commons.collections4.SetUtils;

import java.util.*;
import java.util.stream.Collectors;

public class SimilarityStrUtil {

    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("缴水费");
        list.add("缴热力费");
        list.add("缴燃气费");

        String str = "我想交水费";
        String simiLast = compairSimilarityStr(list, str, 0.3);
        System.out.println(simiLast);
    }

    /**
     * 比较后 选择一个分数 最高的
     *
     * @param target 目标比较的命令列表
     * @param source 不要比较的 命令
     * @param score  给与一个最低要求的分数
     * @return 命中的命令词
     */
    public static String compairSimilarityStr(Collection<String> target, String source, double score) {
        SortedMap<Float, String> map = new TreeMap<Float, String>();
        for (String str : target) {
            float f = cos(source, str);
            map.put(f, str);
        }
        //相似度 低于 指定分数 认为 没有匹配到
        if (score < map.lastKey()) {
            return map.get(map.lastKey());
        } else {
            return null;
        }
    }

    public static List<String> compairSimilarityStr(Collection<String> target, String source, double score, int size) {
        int realySize = size;
        List<String> list = new ArrayList<>();
        if (target == null || target.size() == 0) {
            return list;
        }
        SortedMap<Float, List<String>> map = new TreeMap<Float, List<String>>();
        for (String str : target) {
            float f = cos(source, str);
            if (map.containsKey(f)) {
                List<String> list1 = map.get(f);
                list1.add(str);
            } else {
                List<String> list1 = new ArrayList<>();
                list1.add(str);
                map.put(f, list1);
            }
        }
        //相似度 低于 指定分数 认为 没有匹配到
        for (int i = 0; i < size; i++) {
            if (map == null || map.size() == 0) {
                break;
            }
            if (score < map.lastKey()) {
                List<String> list1 = map.get(map.lastKey());
                boolean a = true;
                for (String s : list1) {
                    if (s.equals(source)) {
                        continue;
                    }
                    if (!list.contains(s)) {
                        list.add(s);
                        a = false;
                    }
                    if (realySize == list.size()) {
                        return list;
                    }
                }
                if (a) {
                    size++;
                }
                map.remove(map.lastKey()); // 移除最后一个键值对
            } else {
                return list;
            }
        }
        return list;
    }


    public static float cos(String a, String b) {
        if (a == null || b == null) {
            return 0F;
        }
        Set<Integer> aChar = a.chars().boxed().collect(Collectors.toSet());
        Set<Integer> bChar = b.chars().boxed().collect(Collectors.toSet());

        // 统计字频
        Map<Integer, Integer> aMap = new HashMap<>();
        Map<Integer, Integer> bMap = new HashMap<>();
        for (Integer a1 : aChar) {
            aMap.put(a1, aMap.getOrDefault(a1, 0) + 1);
        }
        for (Integer b1 : bChar) {
            bMap.put(b1, bMap.getOrDefault(b1, 0) + 1);
        }

        // 向量化
        Set<Integer> union = SetUtils.union(aChar, bChar);
        int[] aVec = new int[union.size()];
        int[] bVec = new int[union.size()];
        List<Integer> collect = new ArrayList<>(union);
        for (int i = 0; i < collect.size(); i++) {
            aVec[i] = aMap.getOrDefault(collect.get(i), 0);
            bVec[i] = bMap.getOrDefault(collect.get(i), 0);
        }

        // 分别计算三个参数
        int p1 = 0;
        for (int i = 0; i < aVec.length; i++) {
            p1 += (aVec[i] * bVec[i]);
        }

        float p2 = 0f;
        for (int i : aVec) {
            p2 += (i * i);
        }
        p2 = (float) Math.sqrt(p2);

        float p3 = 0f;
        for (int i : bVec) {
            p3 += (i * i);
        }
        p3 = (float) Math.sqrt(p3);

        return ((float) p1) / (p2 * p3);
    }
}